package com.pactera.redis.autoconfigure;

import com.pactera.redis.annotation.Lock;
import com.pactera.redis.exception.RedisException;
import com.pactera.redis.utils.LockUtils;
import com.pactera.redis.utils.Md5Utils;
import org.apache.commons.lang3.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;

/**
 * 基于注解分布锁AOP
 *
 * @author: liqingdian
 */
@Aspect
@Configuration
public class LockConfiguration {

    /**
     * 接口调用切入点（拦截带注解的方法）
     *
     * @author: liqingdian
     */
    @Pointcut("@annotation(com.pactera.redis.annotation.Lock)")
    public void redissonLock() {
    }

    @Around(value = "redissonLock()")
    public Object around(ProceedingJoinPoint joinPoint) {
        // 获取方法签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        // 获取方法长名称
        String longName = signature.toLongString();
        // 获取目标类当前方法的缓存锁注解，并获取缓存时间
        StringBuilder builder = new StringBuilder();
        Lock annotation = method.getAnnotation(Lock.class);
        int timeout = annotation.timeout();
        int[] index = annotation.argIndex();
        builder.append(longName);
        // 获取方法参数
        Object[] args = joinPoint.getArgs();
        if (ArrayUtils.isNotEmpty(args) && index[0] != -1) {
            for (int idx : index) {
                if (args.length > idx) {
                    Object arg = args[idx];
                    builder.append("-").append(arg == null ? "null" : arg.hashCode());
                }
            }
        }
        String lockKey = Md5Utils.md5(builder.toString());
        if(LockUtils.locker() == null){
            try {
                return joinPoint.proceed();
            } catch (Throwable throwable) {
                throw new RedisException(throwable);
            }
        }

        try {
            // 添加锁
            LockUtils.lock(lockKey, timeout);
            return joinPoint.proceed();
        } catch (Throwable throwable) {
            throw new RedisException(throwable);
        } finally {
            // 释放锁
            LockUtils.unlock(lockKey);
        }
    }
}
